Day 4開始要認識TypeScript的型別系統。
TypeScript擴充了JavaScript既有的型別系統,包含基本型別(primitive type)和參考型別(reference type),相較於大都與JavaScript相同的基本型別,TypeScript提供更多好用的參考型別,像是interface
、enum
、tuple
。
個人覺得比較值得學習的是參考型別,畢竟實戰中碰到最多的是這類型別,而且也相對複雜,所以今天這篇算是藉著學習TypeScript宣告有型別變數的用法來複習JavaScript型別,希望這幾天不會花太多篇幅在基本型別(汗)。
TypeScript的number型別可以接受十進位(decimal)、二進位(binary)、八進位(octal)和十六進位(hexadecimal)的值,唯獨bigint值還是得指定是bigint
型別。
除了十進位可直接輸入十進位值,其他進位的值須在值的開頭加上識別字符,
0b
或0B
0o
、0O
0x
、0x
number型別變數範例
let decimal: number = 13;
let binary: number = 0b1101; // 或 0B1101
let octal: number = 0o15; // 或 0O15
let hexadecimal: number = 0xB; // 或 0XB
JavaScript從ES2020開始支援bigint型別,因此TypeScript同樣也有提供bigint型別。
bigint是指超過 2^53^-1 的數值,其值最後面要加上n
才是bigint型別的數值,如:999999999999999999n
,
bigint型別變數範例:
let big: bigint = 999999999999999999n;
基本上string型別語法都跟JavaScript一樣,支援雙引號 "
、單引號 '
和反引號 `
string型別範例:
let dbstr: string = "Hello";
let singstr: string = "World";
let backstr: string = ` TypeScript
is
Fun
`;
boolean型別跟JavaScript一樣只允許true
和false
值,其用法也相同:
let real: boolean = true;
let fake: boolean = false;
real = false; // ok
Symbol基本型別用法也與JavaScript相同,是用 Symbol()
來宣告一個Symbol型別變數,例如:
let firstname = "name";
let lastname = "name";
console.log(firstname === lastname); // false
Symbol平常比較少用到,不過原則上就是用來 創造型別 ,所以像上面兩個 Symbol 變數雖然都儲存相同的 "name"
,但事實上是代表兩個不同型別,也就是兩種不同的東西,所以在作相等判斷時,永遠都是 return false
。
TypeScript同樣也有undefined和null,但這兩種型別平常在寫JavaScript的時候造成不少麻煩,所以TypeScript有提供編譯組態和運算子(operator)來處理這兩種型別。
strictNullCheck
tsconfig檔案有提供 strictNullCheck
選項來嚴格限制是否要嚴格限制可能出現這兩種型別的狀況。
若設定此選項為 true
的時候,必須對可能出現 undefined
或 null
的情況作處理;若沒有適當處理,TypeScript編譯器可能會出現以下編譯錯誤:
error TS2531: Object is possibly 'null'.
基本上官方文件是建議可勾選 true
來避免值為 undefined 或 null 時可能造成的程式錯誤。
Non-null type assertion operator !
某些情況下可能會想忽略以上編譯器出現的錯誤,官方文件說明 !
代表著「告訴編譯器,你斷言(assert)這個值不會是null或undefined的狀況」,看起來有點抽象,來看一個使用!
的簡單例子。
假設hello1和hello2允許不傳入 People、可能是undefined且只顯示 hello 的狀況,此時hello1函式的people因為是undefined也就沒有name屬性,所以編譯器就會出現編譯錯誤:
interface People{
name: string,
}
function hello1(people: People | undefined){
console.log(`Hello ${people.name}`) // compile error
}
function hello2(people: People | undefined){
console.log(`Hello ${people!.name}`) // great
}
若是想允許只出現 hello 的狀況發生,可在 people.name 的 people 後面加上 !
,如hello2函式寫法,避免編譯錯誤。
如果是以我自己之前寫React時的狀況為例,有時候我會先把component寫好,如果有資料送進component才會依據送進來的資料改變其狀態並渲染,但有時候某些資料是允許不必傳入某些component,這時就有可能會出現像上面範例一樣的錯誤,這時就可以利用 !
讓程式碼通過型別的檢查。
TypeScript除了以上跟JavaScript一樣的型別,也有提供any
、void
、never
、unknown
等型別,明天就會來了解這三個型別的意思。
另外,以上都是明確宣告變數型別的範例,這個明確宣告型別的行為稱為 type annotation (或是 type signature);不過,如果變數使用的情境比較單純,TypeScript也能從變數初始值自行推導出變數的型別,TypeScript compiler自行推導變數型別的行為則稱為type inference。
最後再看一次Day 1舉的極簡例子來看type annotation和type inference的差別:
/* Type annotation */
let x: number = 10;
x = "20"; // error ( x is number type)
/* Type inference */
let y = "15";
y = 22; // error (y is string type)
參考資料
TypeScript Tutorial
TypeScript Tutorial: A step-by-step guide to learn TypeScript
Non-null assertion operator